# 剑指Offer题解 - Day55
# 剑指 Offer 62. 圆圈中最后剩下的数字
0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。
例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。
示例 1:
输入: n = 5, m = 3
输出:3
1
2
2
限制:
1 <= n <= 10^5
1 <= m <= 10^6
思路:
首先考虑使用暴力法进行题解。假定0~n-1的是循环链表,那么我们只需要每次移动指针m次,将当前节点删除,然后重复动作。最终当链表只剩下一个节点时,直接返回即可。
此做法需要的时间复杂度是O(mn)
,由题目限制可以看出,此时间复杂度是不可以接受的。而且本方法无法通过提交,会超时。因此不考虑此方法。
# 动态规划
本题是著名的 “约瑟夫环” 问题,可使用 动态规划 解决。
/**
* @param {number} n
* @param {number} m
* @return {number}
*/
var lastRemaining = function(n, m) {
let x = 0;
for (let i = 2; i <= n; i++) {
x = (x + m) % i;
}
return x;
};
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 时间复杂度 O(n)。
- 空间复杂度 O(1)。
分析:
我们假设[n,m]
圆圈的最终解为f(n)
,那么[n-1, m]
圆圈的解就是f(n - 1)
。直到[1, m]
圆圈的解为f(1)
。
对于初始状态[n,m]
,删掉第m
个数字后,就得到一个n-1
的圆圈。因为有可能m > n
,因此删除掉的数字是(m - 1)%n
。而下一轮的第一个数字就会从m%n
开始。
此时,m%n
所代表的就是第一轮中的第一位数字0。可以看出,第一位数字的递推公式为x →(x + m %n)%n
。
最终,得出动态转移方程:
f(n) = (f(n - 1) + m%n)%n = (f(n - 1) + m)%n
1
对于初始值[1, m]
,也就是圆圈里只有1个数字,最终返回的就是0。所以f(1) = 0
。
# 总结
本题考查动态规划。约瑟夫环的问题是经典的数学问题,部分同学包括我在内,可能都不太记得该问题。而且动态方程也比较难归纳,难度系数中级。
复杂度方面,n-1
次循环需要时间为O(n)
,声明变量占用常数级别的空间。